package org.jeecg.common.dict.processor;


//import com.google.auto.service.AutoService;
import com.sun.source.tree.Tree;
import com.sun.source.util.TreePath;
import com.sun.tools.javac.api.JavacTrees;
import com.sun.tools.javac.code.Flags;
import com.sun.tools.javac.code.Symbol;
import com.sun.tools.javac.code.Type;
import com.sun.tools.javac.processing.JavacProcessingEnvironment;
import com.sun.tools.javac.tree.JCTree;
import com.sun.tools.javac.tree.TreeMaker;
import com.sun.tools.javac.tree.TreeTranslator;
import com.sun.tools.javac.util.*;
import org.jeecg.common.dict.annotation.DictField;

import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.Messager;
import javax.annotation.processing.ProcessingEnvironment;
import javax.annotation.processing.RoundEnvironment;
import javax.annotation.processing.SupportedAnnotationTypes;
import javax.annotation.processing.SupportedSourceVersion;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.Element;
import javax.lang.model.element.TypeElement;
import java.lang.annotation.Annotation;
import java.util.ArrayList;
import java.util.LinkedHashSet;
import java.util.Set;

/**
 * @author compass
 * @date 2022-10-13
 * @since 1.0
 **/
//@AutoService(Processor.class)
@SupportedAnnotationTypes("org.jeecg.common.dict.annotation.DictField")
@SupportedSourceVersion(SourceVersion.RELEASE_8)
public class DictAnnotationProcessor extends AbstractProcessor {

    String DICT_TEXT_SUFFIX = "_dictText";

    private JavacTrees javacTrees;
    private TreeMaker treeMaker;
    private Messager messager;
    private Names names;

    private JCTree.JCIdent typeVar;

    @Override
    public synchronized void init(ProcessingEnvironment processingEnv) {
        super.init(processingEnv);
        Context context = ((JavacProcessingEnvironment) processingEnv).getContext();
        javacTrees = JavacTrees.instance(context);
        messager = processingEnv.getMessager();
        treeMaker = TreeMaker.instance(context);
        names = Names.instance(context);


        typeVar=treeMaker.Ident(names.fromString("String"));
    }


    /**
     *
     * @param a
     * @param roundEnv
     * @return
     */
    public Set<Element> getElementsAnnotatedWith(Class<? extends Annotation> a, RoundEnvironment roundEnv){
        //1.获取所有带 注解a 的元素（属性）
        Set<? extends Element> sets = roundEnv.getElementsAnnotatedWith(a);
        Set<Element> distinctSet = new LinkedHashSet<>();
        sets.stream().forEach(element->{
            //2.根据属性得到类的Element ，并且去重复
            distinctSet.add(((Symbol.VarSymbol) element).owner);
        });
        return distinctSet;
    }


    @Override
    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
        //1.获取所有类
        //Set<? extends Element> set = roundEnv.getRootElements();
        Set<? extends Element> set = this.getElementsAnnotatedWith(DictField.class,roundEnv);
        //2.遍历所有类
        for (Element element : set) {
            JCTree jcTree = javacTrees.getTree(element);
            treeMaker.pos=jcTree.pos;
            jcTree.accept(new TreeTranslator(){
                @Override
                public void visitClassDef(JCTree.JCClassDecl jcClassDecl) {

                    //3.访问类结构
                    jcClassDecl.defs.stream()
                            .filter(it->it.getKind().equals(Tree.Kind.VARIABLE))
                            .map(it->(JCTree.JCVariableDecl) it).forEach(it->{
                                //4.读取属性上的注解 @Dict
                                List<JCTree.JCAnnotation> annotationList = it.mods.getAnnotations();
                                annotationList.stream().filter(jcAnnotation -> jcAnnotation.type.toString().equals(DictField.class.getTypeName()))
                                        .forEach(jcAnnotation -> {
                                            System.out.println("----------------------");
                                            System.out.println(it);
                                            addImport(element,"org.jeecg.common.dict.annotation","TableField");
//                                            addImport(element,"com.baomidou.mybatisplus.annotation","TableField");
                                            //5.添加属性和方法
                                            jcClassDecl.defs = jcClassDecl.defs.append(genVariableDecl(it));
                                            jcClassDecl.defs = jcClassDecl.defs.append(genGetterMethod(it));
                                            jcClassDecl.defs = jcClassDecl.defs.append(genSetterMethod(it));

                                        });
                    });
                    super.visitClassDef(jcClassDecl);
                }
            });
        }

        return true;
    }


    private void addImport(Element element, String packageName, String className) {

        System.out.println("addImport:[" + packageName + "." + className+"] to ["+element.getSimpleName()+DICT_TEXT_SUFFIX+"]");

        TreePath treePath = javacTrees.getPath(element);
        Tree leaf = treePath.getLeaf();
        if (treePath.getCompilationUnit() instanceof JCTree.JCCompilationUnit && leaf instanceof JCTree) {
            JCTree.JCCompilationUnit jccu = (JCTree.JCCompilationUnit) treePath.getCompilationUnit();

            for (JCTree jcTree : jccu.getImports()) {
                if (jcTree != null && jcTree instanceof JCTree.JCImport) {
                    JCTree.JCImport jcImport = (JCTree.JCImport) jcTree;
                    if (jcImport.qualid != null && jcImport.qualid instanceof JCTree.JCFieldAccess) {
                        JCTree.JCFieldAccess jcFieldAccess = (JCTree.JCFieldAccess) jcImport.qualid;
                        try {
                            if (packageName.equals(jcFieldAccess.selected.toString()) && className.equals(jcFieldAccess.name.toString())) {
                                return;
                            }
                        } catch (NullPointerException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }
            java.util.List<JCTree> trees = new ArrayList<>();
            trees.addAll(jccu.defs);
            JCTree.JCIdent ident = treeMaker.Ident(names.fromString(packageName));
            JCTree.JCImport jcImport = treeMaker.Import(treeMaker.Select(
                    ident, names.fromString(className)), false);
            if (!trees.contains(jcImport)) {
                trees.add(0, jcImport);
            }
            jccu.defs = List.from(trees);
        }
    }


    /**
     * 添加一个 field
     * @param jcVariableDecl
     * @return
     */
    private JCTree.JCVariableDecl genVariableDecl(JCTree.JCVariableDecl jcVariableDecl){

        /**
         * 构建一个 field 或 method 的注解
         * JCTree.JCAnnotation annotation = treeMaker.Annotation(
         *                 treeMaker.Ident(names.fromString("HTTP_MAPPING_CLASS_NAME"))
         *                 , List.of(treeMaker.Assign(
         *                         treeMaker.Ident(names.fromString("HTTP_MAPPING_ATTR_VALUE"))
         *                         , treeMaker.Literal("HTTP_MAPPING_ATTR_ASSIGN"))
         *                 )
         *         );
         *
         * JCTree.JCAnnotation annotation = treeMaker.Annotation(
         *                 treeMaker.Ident(names.fromString("TableField"))
         *                 , List.nil()
         *  );
         */
        JCTree.JCAnnotation annotation = treeMaker.Annotation(
                treeMaker.Ident(names.fromString("TableField")),
                List.of(treeMaker.Assign(
                       treeMaker.Ident(names.fromString("exist"))
                      , treeMaker.Literal(false))
               )
        );

        JCTree.JCModifiers jcModifiers = treeMaker.Modifiers(Flags.PRIVATE, List.of(annotation));

        JCTree.JCVariableDecl newVariable = treeMaker.VarDef(jcModifiers, // 访问标识
                this.getFieldName(jcVariableDecl.getName()), // 名称
                typeVar, // 类型
                null); // 初始化语句
//        new JCTree.JCAnnotation

//        newVariable.mods.annotations = Arrays.asList();

        return newVariable;


    }


    /**
     * 添加getter方法
     * @param jcVariableDecl
     * @return
     */
    private JCTree.JCMethodDecl genGetterMethod(JCTree.JCVariableDecl jcVariableDecl){
        JCTree.JCIdent _this = treeMaker.Ident(names.fromString("this"));
        Name name = jcVariableDecl.getName();
        JCTree.JCFieldAccess select = treeMaker.Select(_this, this.getFieldName(name));
        JCTree.JCReturn returnStatement = treeMaker.Return(select);

        ListBuffer<JCTree.JCStatement> statements = new ListBuffer<>();
        statements.append(returnStatement);

        JCTree.JCModifiers modifiers = treeMaker.Modifiers(Flags.PUBLIC);

        Name getMethodName = getGetMethodName(jcVariableDecl.getName());

        JCTree.JCExpression returnMethodType = typeVar;

        JCTree.JCBlock body = treeMaker.Block(0, statements.toList());

        List<JCTree.JCTypeParameter> methodGenericParamList = List.nil();

        List<JCTree.JCVariableDecl> parameterList = List.nil();

        List<JCTree.JCExpression> throwList = List.nil();

        return treeMaker.MethodDef(modifiers, getMethodName, returnMethodType, methodGenericParamList, parameterList, throwList, body, null);

    }

    /**
     * 添加setter方法
     * @param jcVariableDecl
     * @return
     */
    public  JCTree.JCMethodDecl genSetterMethod(JCTree.JCVariableDecl jcVariableDecl){

        JCTree.JCIdent _this = treeMaker.Ident(names.fromString("this"));
        Name name = jcVariableDecl.getName();
        JCTree.JCFieldAccess select = treeMaker.Select(_this, this.getFieldName(name));
        JCTree.JCAssign statementAssign = treeMaker.Assign(select, treeMaker.Ident(jcVariableDecl.getName()));
        JCTree.JCExpressionStatement statement = treeMaker.Exec(statementAssign);
        ListBuffer<JCTree.JCStatement> statements = new ListBuffer<>();
        statements.append(statement);

        JCTree.JCVariableDecl params = treeMaker.VarDef(
                treeMaker.Modifiers(Flags.PARAMETER, List.nil()),
                jcVariableDecl.name,
                typeVar,
                null
        );

        JCTree.JCModifiers modifiers = treeMaker.Modifiers(Flags.PUBLIC);

        Name setMethodName = getSetMethodName(jcVariableDecl.getName());

        JCTree.JCExpression returnMethodType = treeMaker.Type(new Type.JCVoidType());

        JCTree.JCBlock body = treeMaker.Block(0, statements.toList());

        List<JCTree.JCTypeParameter> methodGenericParamList = List.nil();

        List<JCTree.JCVariableDecl> parameterList = List.of(params);

        List<JCTree.JCExpression> throwList = List.nil();

        return treeMaker.MethodDef(modifiers, setMethodName, returnMethodType, methodGenericParamList, parameterList, throwList, body, null);
    }

    private Name getFieldName(Name name){
        String filedName = name.toString();
        return names.fromString(filedName+DICT_TEXT_SUFFIX);
    }

    private Name getGetMethodName(Name name){
        String filedName = name.toString();
        return names.fromString("get"+filedName.substring(0,1).toUpperCase()+filedName.substring(1)+DICT_TEXT_SUFFIX);
    }

    private Name getSetMethodName(Name name){
        String filedName = name.toString();
        return names.fromString("set"+filedName.substring(0,1).toUpperCase()+filedName.substring(1)+DICT_TEXT_SUFFIX);
    }


//

}
